package com.luo.demo.oidc.combo.service;

import com.luo.demo.oidc.combo.enums.LoginTypeEnum;
import com.luo.sc.oidc.authserver.handler.login.UniLoginUserDetails;
import com.luo.sc.oidc.authserver.handler.login.UniLoginUserDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 符合登录用户服务
 *
 * @author luohq
 * @version 1.0.0
 * @date 2022-03-07 11:22
 */
@Service
@Slf4j
public class ComboLoginUserDetailsServiceImpl implements UniLoginUserDetailsService {

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private PhoneSmsCodeService phoneSmsCodeService;

    /**
     * 参数名常量
     */
    public static final String PARAM_TYPE = "type";

    /**
     * map(登录类型, 对应的认证服务实现类)
     */
    private Map<String, UniLoginUserDetailsService> authType2ServiceImplMap = new HashMap<>(2);

    @PostConstruct
    private void init() {
        /** 初始化map(登录类型, 对应的认证服务) */
        this.authType2ServiceImplMap.put(LoginTypeEnum.USERNAME.getTypeStr(), new UsernameLoginUserDetailsServiceImpl(this.passwordEncoder));
        this.authType2ServiceImplMap.put(LoginTypeEnum.PHONE.getTypeStr(), new PhoneNoLoginUserDetailsServiceImpl(this.phoneSmsCodeService));
    }


    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> authParams) throws UsernameNotFoundException {
        String authType = authParams.get(PARAM_TYPE);

        //根据认证类型调用不同逻辑
        return this.authType2ServiceImplMap.get(authType)
                .loadUserByAuthParams(authParams);
    }


    @Override
    public void authenticateUser(Map<String, String> authParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        String authType = authParams.get(PARAM_TYPE);
        //根据认证类型调用不同逻辑
        this.authType2ServiceImplMap.get(authType)
                .authenticateUser(authParams, uniLoginUserDetails);
    }


    /**
     * 用户账号密码认证服务
     */
    private class UsernameLoginUserDetailsServiceImpl implements UniLoginUserDetailsService {

        /**
         * 密码编码器
         */
        private PasswordEncoder passwordEncoder;

        /**
         * 参数名称常量
         */
        private final String PARAM_USERNAME = "username";
        private final String PARAM_PASSWORD = "password";

        /**
         * 用户账号Map（模拟用户数据库）
         */
        private Map<String, String> user2PasswordMap = new HashMap<>(1);
        private Map<String, List<String>> user2AuthoritiesMap = new HashMap<>(1);

        /**
         * 临时权限
         */
        private List<String> tempAuthorityList = Arrays.asList("temp");


        public UsernameLoginUserDetailsServiceImpl(PasswordEncoder passwordEncoder) {
            this.passwordEncoder = passwordEncoder;
            //初始化用户数据MOCK
            this.initUserStore();
        }

        /**
         * MOCK初始化用户数据
         */
        private void initUserStore() {
            //初始化用户数据
            this.user2PasswordMap.put("luo", this.passwordEncoder.encode("123456"));
            this.user2AuthoritiesMap.put("luo", tempAuthorityList);
        }


        @Override
        public UniLoginUserDetails loadUserByAuthParams(Map<String, String> authParams) throws UsernameNotFoundException {
            //获取请求参数
            String username = authParams.get(PARAM_USERNAME);
            //查询用户信息
            if (!this.user2PasswordMap.containsKey(username)) {
                throw new UsernameNotFoundException("用户名不存在");
            }
            return UniLoginUserDetails.builder()
                    .username(username)
                    .password(this.user2PasswordMap.get(username))
                    .authorities(this.user2AuthoritiesMap.get(username))
                    .build();
        }

        @Override
        public void authenticateUser(Map<String, String> authParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
            //比较密码是否一致
            Boolean passwordMatch = this.passwordEncoder.matches(authParams.get(PARAM_PASSWORD), uniLoginUserDetails.getPassword());
            //若不一致，则抛出异常
            if (!passwordMatch) {
                throw new BadCredentialsException("密码不正确");
            }
        }
    }


    /**
     * 手机号验证码认证服务
     */
    private class PhoneNoLoginUserDetailsServiceImpl implements UniLoginUserDetailsService {
        /**
         * 参数名常量
         */
        private final String PARAM_PHONE_NO = "phoneNo";
        private final String PARAM_SMS_CODE = "smsCode";
        /**
         * 临时权限
         */
        private List<String> tempAuthorityList = Arrays.asList("temp");

        /**
         * 手机验证码服务
         */
        private PhoneSmsCodeService phoneSmsCodeService;

        public PhoneNoLoginUserDetailsServiceImpl(PhoneSmsCodeService phoneSmsCodeService) {
            this.phoneSmsCodeService = phoneSmsCodeService;
        }

        @Override
        public UniLoginUserDetails loadUserByAuthParams(Map<String, String> authParams) throws UsernameNotFoundException {
            //获取请求参数
            String phoneNo = authParams.get(PARAM_PHONE_NO);
            String smsCode = authParams.get(PARAM_SMS_CODE);

            /** 验证手机验证码 */
            this.phoneSmsCodeService.validateSmsCode(phoneNo, smsCode);

            //TODO 根据手机号查询 或者 注册用户信息

            //返回用户信息
            return UniLoginUserDetails.builder()
                    .username(phoneNo)
                    .password(smsCode)
                    .authorities(tempAuthorityList)
                    .build();
        }

        @Override
        public void authenticateUser(Map<String, String> authParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
            //空实现 - 已在loadUserByAuthParams方法中进行过验证
        }
    }


}
